package cn.jetpiece.cloud.core.filters;

import cn.hutool.core.util.StrUtil;
import cn.jetpiece.cloud.core.consts.GatewayConstants;
import cn.jetpiece.cloud.core.exception.GatewayException;
import cn.jetpiece.cloud.core.exception.enums.GatewayExceptionEnum;
import cn.jetpiece.cloud.core.util.AuthenticationUtils;
import cn.jetpiece.cloud.feign.instance.FeignInstance;
import cn.jetpiece.cloud.joss.api.accossor.AccessorApi;
import cn.jetpiece.cloud.joss.api.constant.AccessorCache;
import cn.jetpiece.cloud.joss.api.constant.BaseOssConstants;
import cn.jetpiece.cloud.model.response.Result;
import cn.jetpiece.cloud.redis.utils.RedisUtil;
import cn.jetpiece.cloud.resource.annotation.model.ResourceParam;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

/**
 * JetPiece对象存储网关Filter
 *
 * @author XuJZ
 */
@Component
public class JossAccessGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    @Override
    public GatewayFilter apply(Object config) {
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String ignoreUrl = request.getHeaders().getFirst(GatewayConstants.IGNORE_URL);
            /* 忽略校验的地址直接放行 */
            if (StrUtil.isNotBlank(ignoreUrl)) {
                return chain.filter(exchange.mutate().request(request).build());
            }
            /* JossToken验证 */
            String jossToken = request.getQueryParams().getFirst(BaseOssConstants.JOSS_TOKEN_PARAM);
            if (StrUtil.isNotBlank(jossToken)) {
                String requestUri = request.getURI().getPath().replaceFirst("/joss", "");
                String uri = (String) RedisUtil.getInstance().getRedisTemplate().opsForValue().get(BaseOssConstants.JOSS_TOKEN_CACHE + jossToken);
                if (uri != null && requestUri.startsWith(uri)) {
                    /* JossToken验证成功，标识为认证成功 */
                    request = request.mutate().header(BaseOssConstants.JOSS_AUTH_HEADER, "1").build();
                    return chain.filter(exchange.mutate().request(request).build());
                }
            }
            //授权验证
            GatewayExceptionEnum anEnum = checkAccess(request);
            if (anEnum == null) {
                /* 授权校验成功，标识为认证成功 */
                request = request.mutate().header(BaseOssConstants.JOSS_AUTH_HEADER, "1").build();
                return chain.filter(exchange.mutate().request(request).build());
            }
            /* UC统一token验证 */
            /* 获取资源 */
            ResourceParam resourceParam = AuthenticationUtils.getResource(request);
            //获取token
            String token = request.getHeaders().getFirst(GatewayConstants.AUTH_HEADER);
            /* 校验token */
            anEnum = AuthenticationUtils.checkToken(token);
            if (anEnum == null) {
                request = request.mutate().header(BaseOssConstants.JOSS_AUTH_HEADER, "1").build();
                return chain.filter(exchange.mutate().request(request).build());
            }
            anEnum = AuthenticationUtils.checkResource(resourceParam);
            if (anEnum == null) {
                request = request.mutate().header(BaseOssConstants.JOSS_AUTH_HEADER, "0").build();
                return chain.filter(exchange.mutate().request(request).build());
            }
            throw new GatewayException(anEnum);
        });
    }

    @SneakyThrows
    private GatewayExceptionEnum checkAccess(ServerHttpRequest request) {
        //参数是否为空
        String sign = request.getQueryParams().getFirst("sign");
        if (sign == null || sign.length() == 0) {
            return GatewayExceptionEnum.SIGN_EMPTY;
        }
        String accessKey = request.getQueryParams().getFirst("accessKey");
        if (accessKey == null || accessKey.length() == 0) {
            return GatewayExceptionEnum.ACCESS_KEY_EMPTY;
        }
        request = request.mutate().header(BaseOssConstants.ACCESS_KEY, accessKey).build();

        String timestamp = request.getQueryParams().getFirst("timestamp");
        if (timestamp == null || timestamp.length() == 0) {
            return GatewayExceptionEnum.TIME_STAMP_EMPTY;
        }
        //验证授权
        AccessorCache accessor = (AccessorCache) RedisUtil.getInstance().getRedisTemplate().boundHashOps(BaseOssConstants.ACCESSOR).get(accessKey);
        if (accessor == null) {
            AccessorApi accessorApi = FeignInstance.getInstance().newInstance(AccessorApi.class, "jetpiece-joss");
            CompletableFuture<Result<AccessorCache>> future = CompletableFuture.supplyAsync(() -> accessorApi.getByAccessKey(accessKey));
            accessor = future.get().getResult();
        }
        if (accessor == null) {
            return GatewayExceptionEnum.ACCESSOR_NOT_EXISTED;
        }
        if (accessor.getIsEnable() != 1) {
            return GatewayExceptionEnum.ACCESSOR_NOT_EXISTED;
        }
        //验证时间戳
        long diff = Math.abs(System.currentTimeMillis() - Long.parseLong(timestamp));
        long requestValidTime;
        if (accessor.getRequestValidTime() == null) {
            requestValidTime = BaseOssConstants.DEFAULT_REQUEST_VALID_TIME;
        } else {
            requestValidTime = accessor.getRequestValidTime();
        }
        if (requestValidTime < diff) {
            return GatewayExceptionEnum.TIME_STAMP_INVALID;
        }
        //验证签名
        if (!sign.equals(DigestUtils.md5Hex(accessor.getSecretKey() + accessKey + timestamp + accessor.getSecretKey()))) {
            return GatewayExceptionEnum.SIGN_ERROR;
        }
        return null;
    }
}

